//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Text;
using OpenLS.Spreadsheet.Expressions;
using OpenLS.Spreadsheet.Formats.Biff.Tokens;

namespace OpenLS.Spreadsheet.Formats.Biff.Records
{
    /// <summary>
    /// Lbl in Excel
    /// </summary>
    internal class LblRecord : WorkbookLevelRecord
    {
        private readonly string _description;
        private NamedParsedFormula _formula;
        private string _name;
        private string _statusBar;
        private BuiltInName? _builtInName;
        private bool _fCalcExp;
        private bool _fFunc;
        private int _fGrp;
        private bool _fHidden;
        private bool _fOb;
        private bool _fProc;
        private bool _fPublished;
        private bool _fWorkbookParam;
        private ushort _iTab;
        private byte _keyboardShortcut;
        public static LblRecord CreateAutoFilterRecord(BiffWriterWorkbookContext context, Range range)
        {
            LblRecord result = new LblRecord();
            result._fHidden = true;
            result._builtInName = BuiltInName._FilterDatabase;
            result._fHidden = true;
            result._iTab = (ushort)(context.GetSheetIndex(range.Worksheet) + 1);
            RangeReference rr = range.ToReference();
            result._formula = NamedParsedFormula.GetFormulaValue<NamedParsedFormula>(context, new RangeReferenceExpression(rr, null), null);
            return result;

        }
        public LblRecord(BiffWriterWorkbookContext context, string definedName)
        {
            Contract.Requires(definedName != null);
            _name =  definedName;
            BuiltInName n;
            _formula = null;
            context.RegisterDefinedName(definedName);
        }
        
        public LblRecord(BiffWriterWorkbookContext context, DefinedName definedName)
        {
            Contract.Requires(definedName != null);
            _name =  definedName.Name;
            BuiltInName n;
            if (EnumHelper.TryParse(definedName.Name, out n))
            {
                _builtInName = n;
                _fHidden = true;
            }
            if (definedName.LocalSheet != null)
            {
                this._iTab = (ushort) (context.GetSheetIndex((Worksheet) definedName.LocalSheet) + 1);
            }
            _formula = NamedParsedFormula.GetFormulaValue<NamedParsedFormula>(context, definedName.Expression, null);
            _description = definedName.Description;
            context.RegisterDefinedName(definedName);
        }

        public LblRecord()
        {
        }


        internal override BiffRecordType RecordType
        {
            get { return BiffRecordType.Lbl; }
        }

        protected ushort AbcdeffGrpghij
        {
            get
            {
                var result = (ushort) (_fGrp << 6);
                if (_fHidden)
                    result |= 0x0001;
                if (_fFunc)
                    result |= 0x0002;
                if (_fOb)
                    result |= 0x0004;
                if (_fProc)
                    result |= 0x0008;
                if (_fCalcExp)
                    result |= 0x0010;
                if (GetBuiltInName() != null)
                    result |= 0x0020;
                if (false)
                    result |= 0x1000;
                if (_fPublished)
                    result |= 0x2000;
                if (_fWorkbookParam)
                    result |= 0x4000;
                if (false)
                    result |= 0x8000;
                return result;
            }
        }


        private string ActualName
        {
            get
            {
                Contract.Ensures(!string.IsNullOrEmpty(Contract.Result<string>()));
                if (GetBuiltInName() != null)
                    return GetBuiltInName().Value.ToString();
                var sb = new StringBuilder();
                var s = (string) _name;
                foreach (char c in s.Trim())
                {
                    char cc = c;
                    if (!char.IsLetterOrDigit(c) && c != '.' && c != '_')
                    {
                        cc = '_';
                    }
                    sb.Append(cc);
                }
                return sb.ToString();
            }
        }

        public ushort ITab
        {
            get { return _iTab; }
        }

        public BuiltInName? GetBuiltInName()
        {
            { return _builtInName; }
        }


        internal override void  WriteContent(BiffWriterWorkbookContext context)
        {
            context.Write(AbcdeffGrpghij);
            context.Write(_keyboardShortcut);

            if (GetBuiltInName() != null)
                context.Write((byte)1);
            else
            {
                XLUnicodeStringNoCch n = (XLUnicodeStringNoCch) _name;
                context.Write((byte) n.ByteLength);
            }
            var formulaContext = new FormulaWriteContext(context.Writer, FormulaReadContextType.Default);
            ushort count;
            count = _formula != null ? _formula.TotalTokenByteLength(formulaContext.Type) : (ushort)0;
            context.WriteUInt16(count); //\\sure?
            context.WriteUInt16(0);
            context.WriteUInt16(ITab);
            context.WriteUInt32(0);
            if (GetBuiltInName() != null)
            {
                context.Write((byte) 0);
                context.Write((byte)GetBuiltInName().Value);
            }
            else
            {
                XLUnicodeStringNoCch n = (XLUnicodeStringNoCch)_name;
                n.Write(context.Writer);
            }
            if (_formula != null)
            _formula.WriteNamed(formulaContext);
        }

        protected override void ReadContent(BiffReaderContext context, int length)
        {
#if DEBUG
            long oldPosition = context.BaseStream.Position;
#endif
            short abcdeffGrpghij = context.ReadInt16();

            _fHidden = (abcdeffGrpghij & 0x1) != 0;
            _fFunc = (abcdeffGrpghij & 02) != 0;
            _fOb = (abcdeffGrpghij & 0x4) != 0;
            _fProc = (abcdeffGrpghij & 0x8) != 0;
            _fCalcExp = (abcdeffGrpghij & 0x10) != 0;
            var fBuiltin = (abcdeffGrpghij & 0x20) != 0;
            _fGrp = ((abcdeffGrpghij >> 6) & 0x3F);
            int reserved1 = (abcdeffGrpghij & 0x1000);
            Contract.Assert(reserved1 == 0);
            _fPublished = (abcdeffGrpghij & 0x2000) != 0;
            _fWorkbookParam = (abcdeffGrpghij & 0x4000) != 0;
            int reserved2 = (abcdeffGrpghij & 0x8000);
            Contract.Assert(reserved2 == 0);
            _keyboardShortcut = context.ReadByte();
            byte cch = context.ReadByte();
            ushort cce = context.ReadUInt16();
            context.ReadUInt16();
            _iTab = context.ReadUInt16();
            byte unused1 = context.ReadByte();
            byte unused2 = context.ReadByte();
            byte unused3 = context.ReadByte();
            byte unused4 = context.ReadByte();
            if (fBuiltin)
            {

                XLUnicodeStringNoCch n = new XLUnicodeStringNoCch();

                n.Read(context.Reader, cch);
                string s = (string) n;
                this._builtInName = (BuiltInName)s[0];
                //\\_name = _builtInName.ToString();
            }
            else
            {
                XLUnicodeStringNoCch n = new XLUnicodeStringNoCch();

                n.Read(context.Reader, cch);
                _name = (string) n;
            }
            Contract.Assert(AbcdeffGrpghij == abcdeffGrpghij);

#if DEBUG
            var posBeforeFormula = (int) context.BaseStream.Position;
#endif
            _formula = new NamedParsedFormula();
            _formula.Read(new FormulaReadContext(context.Reader, FormulaReadContextType.Default), cce);
#if DEBUG
            var posAfterFormula = (int) context.BaseStream.Position;
            Contract.Assert(posAfterFormula - posBeforeFormula >= cce);
           //\\ Debug.WriteLine("adding name " + _name);
            long newPosition = context.BaseStream.Position;
            Contract.Assert(newPosition - oldPosition == length);
#endif
        }

        public override void Apply(BiffReaderWorkbookContext context)
        {
            Debug.WriteLine("Applying " + this.GetType());
            Workbook workbook = context.Workbook;
            var definedName = new DefinedName(workbook, ActualName)
                                  {
                                      Description = _description,
                                      StatusBar = _statusBar
                                  };
            context.DefinedNames.Add(definedName);
            
            Contract.Assert(workbook.IsValidName(definedName.Name, null));
            context.GetActions(ActionType.DefinedName).Add(delegate
                                                               {
                                                                   if (_fOb)
                                                                   {
                                                                      
                                                                   }
                                                                   else
                                                                   {

                                                                       Debug.WriteLine("Defining name");
                                                                       if (ITab > 0)
                                                                       {
                                                                           if (ITab - 1 < workbook.Sheets.Count)
                                                                               definedName.LocalSheet =
                                                                                   workbook.Sheets[ITab - 1];
                                                                       }
                                                                       workbook.AddDefinedName(definedName);
                                                                   }
                                                               });
            if (!_fOb)
            context.GetActions(ActionType.ResolveName).Add(delegate
                                                               {
                                                                   if (_formula.TokenCount == 0)
                                                                   {
                                                                       return;
                                                                   }
                                                                   try
                                                                   {
                                                                       Expression o = _formula.GetExpression(context);
                                                                       string v = o.GetText(new TextContext(null));
                                                                       definedName.Formula = v;
                                                                        if (GetBuiltInName() != null && GetBuiltInName().Value == BuiltInName._FilterDatabase)
                                                                        {
                                                                            context.AddFilterDatabase(o);
                                                                        }

                                                                   }
                                                                   catch (InvalidFormulaException)
                                                                   {
                                                                       definedName.Formula = Errors.NaError.Code;
                                                                   }
                                                               });
        }

        #region Nested type: DefinedNameFlag

        #endregion

        #region Nested type: builtinNames

        #endregion

    }
}